package task

import (
	"context"
	"encoding/json"
	"errors"
	"smart-flow/server/config"
	"smart-flow/server/job"
	"smart-flow/server/logger"
	"smart-flow/server/redis"
	"sync/atomic"
	"time"

	"go.uber.org/zap"
)

type FlowJobFun func(flowJob *job.FlowJob)

type PausingFun func() bool

type FlowTask struct {
	flowJob     *job.FlowJob
	flowJobFun  FlowJobFun
	pausingFun  PausingFun
	locked      atomic.Bool // 是否被锁定
	unscheduled atomic.Bool // 是否不可被调度
}

func (f *FlowTask) Init(jobJson []byte, flowJobFun FlowJobFun, pausingFun PausingFun) bool {
	f.flowJob = new(job.FlowJob)
	err := json.Unmarshal(jobJson, f.flowJob)
	if err != nil {
		logger.Logger.Error("[任务] json反序列化异常", zap.Error(err))
		return false
	}
	if !f.validate() {
		return false
	}
	if f.flowJob.Status == config.TaskFinish {
		f.locked.Store(true)
		f.unscheduled.Store(true)
	}
	f.flowJobFun = flowJobFun
	f.pausingFun = pausingFun
	return true
}

func (f *FlowTask) validate() bool {
	if f.flowJob.RunCycle < 0 || f.flowJob.RunCycle > 1 {
		logger.Logger.Error("[任务] 运行周期设置错误", zap.Any("job", f.flowJob))
		return false
	}
	if f.flowJob.RunDuration < 0 {
		logger.Logger.Error("[任务] 运行时长设置错误", zap.Any("job", f.flowJob))
		return false
	}
	if f.flowJob.RunRate < 100 {
		logger.Logger.Error("[任务] 运行速率设置错误", zap.Any("job", f.flowJob))
		return false
	}
	return true
}

func (f *FlowTask) GetJob() *job.FlowJob {
	return f.flowJob
}

func (f *FlowTask) Id() string {
	return f.flowJob.TaskId
}

func (f *FlowTask) HumanIsp() string {
	return config.GetIspHuman(f.flowJob.Isp)
}

func (f *FlowTask) Run() {
	if f.locked.Load() || f.pausingFun() {
		return
	}
	if !f.executed() {
		f.flowJob.FirstRunTime = time.Now().Unix()
	}
	f.flowJob.Status = config.TaskDoing
	f.save()
	logger.Logger.Info("[任务] 执行中...", zap.Any("job", f.flowJob))
	// time.Sleep(10 * time.Second)
	f.flowJobFun(f.flowJob)
	if f.locked.Load() || f.pausingFun() {
		return
	}
	f.flowJob.Status = config.TaskDoingWait
	f.save()
}

func (f *FlowTask) CalcSchedulerTime() (time.Time, time.Duration, error) {
	now := time.Now()
	var startRunTime time.Time
	startTime, stopTime := f.runTimeRange()
	if !f.limited() {
		startRunTime = now.Add(5 * time.Second)
		return startRunTime, time.Duration(0), nil
	}
	if stopTime.Before(now) {
		f.Finish()
		if f.cycled() {
			startRunTime = startTime.Add(24 * time.Hour)
			f.locked.Store(false)
		} else {
			return time.Time{}, time.Duration(0), errors.New("任务已结束")
		}
	} else {
		if startTime.Before(now) {
			startRunTime = now.Add(5 * time.Second)
		} else {
			startRunTime = startTime
		}
	}
	f.save()
	return startRunTime, time.Until(stopTime), nil
}

func (f *FlowTask) Finish() {
	f.locked.Store(true)
	if f.cycled() {
		f.flowJob.StartTime = 0
		f.flowJob.FirstRunTime = 0
		f.flowJob.Status = config.TaskCycleReady
	} else {
		f.flowJob.Status = config.TaskFinish
	}
	f.save()
}

func (f *FlowTask) Unschedule() bool {
	return f.unscheduled.Load()
}

func (f *FlowTask) Isp() string {
	return f.flowJob.Isp
}

func (f *FlowTask) runTimeRange() (time.Time, time.Time) {
	now := time.Now()
	var startTime, stopTime time.Time
	if f.flowJob.StartTime > 0 {
		startTime = time.Unix(f.flowJob.StartTime, 0)
	} else {
		startTime = time.Date(now.Year(), now.Month(), now.Day(),
			f.flowJob.RunTimeHour, f.flowJob.RunTimeMinute, f.flowJob.RunTimeSecond, 0, time.Local)
		f.flowJob.StartTime = startTime.Unix()
	}
	if !f.limited() {
		stopTime = time.Unix(0, 0)
	} else {
		stopTime = startTime.Add(time.Duration(f.flowJob.RunDuration) * time.Minute)
	}
	f.flowJob.StopTime = stopTime.Unix()
	f.save()
	return startTime, stopTime
}

func (f *FlowTask) executed() bool {
	return f.flowJob.FirstRunTime > 0
}

func (f *FlowTask) limited() bool {
	return f.flowJob.RunDuration > 0
}

func (f *FlowTask) cycled() bool {
	return f.flowJob.RunCycle > 0
}

func (f *FlowTask) save() {
	taskJson, _ := json.Marshal(f.flowJob)
	redis.RDB.HSet(context.Background(), config.FlowTaskKey, f.flowJob.Isp, taskJson)
}
